<?php
namespace app\modules\supplier\call;
use think\Db;
use think\Hook;
use app\common\library\Auth as AuthLibrary;

class Auth extends CallBase
{
	protected static $_error = [];

	protected static function _initCall($force = false)
	{
		parent::_initBase($force);
		if ($force || self::_getInit() === false) {
			self::$_client_device = get_client_device(1);
			self::$_client_ip = self::$request->ip(1);
			self::_setInit(true);
		}
	}

	/**
	 * 用户登录状态接口
	 * @param $auto_login bool 用户未登录时,是否使用HTTP请求(AuthToken)和Cookie(AuthData)尝试自动登录
	 * @return bool;
	 */
	final public static function isLogin($auto_login = true)
	{
		self::_initCall();
		if (self::$_is_login && is_array(self::$_account) && !empty(self::$_account)) {
			if (data_auth_sign(self::$_account) == session('account_sign')) {
				self::$_account['activity_time'] = TIMESTAMP;
				self::$_account['activity_ip'] = self::$_client_ip;
				session('account', self::$_account);
				session('account_sign', data_auth_sign(self::$_account));
				if(false==self::$_account['is_remember'] || self::$_account['is_remember']<1){
					session('account_exptime', TIMESTAMP + self::$_auth_expire_interval);
				}
				return true;
			} else {
				// Db::name('sys_user')->where(['user_id' => self::$_account['user_id']])->update(['has_online'=>0]);
				Db::name('supplier_auth')->where(['supplier_user_id' => self::$_account['supplier_user_id'], 'auth_token' => self::_clientAuthTokenEncode(self::$_account['auth_token'])])->delete();
				self::$_error[] = "用户回话异常或已过期";
				// $log = ['user_id' => self::$_account['user_id'], 'action_info' => '用户回话异常或已过期，强制注销了客户端登录状态'];
				// Hook::listen('user_log_action', $log);
				session('account', null);
				session('account_sign', null);
				session('account_exptime', null);
			}
		}
		//未登录状态下，是否自动尝试认证登陆
		if ($auto_login) {
			//Request 请求是否存在Auth Data
			if (self::$request->isPost()) {
				$auth_token = self::$request->post('auth_token', '', 'trim,strtolower');
			} else {
				// Rewrite 下获取不到 PATH_INFO伪装的GET变量,为了兼容这里只能用 request->param
				// request->param 方法会把当前请求类型的参数和PATH_INFO变量以及GET请求合并。
				$auth_token = self::$request->param('auth_token', '', 'trim,strtolower');
			}
			if ($auth_token && !empty($auth_token) && self::_loginAuthData($auth_token, false)) {
				return true;
			}
			//本地Cookie是否存在AuthData
			$auth_data = cookie('client_account');
			if ($auth_data && !empty($auth_data) && self::_loginAuthData($auth_data, true)) {
				return true;
			} else {
				cookie('client_account', null);
			}
		}
		return false;
	}


	private static function _loginAuthData($auth_data = '', $data_is_decrypt = true)
	{
		$auth_data = trim($auth_data);
		if (!$auth_data || empty($auth_data)) {
			return false;
		}
		//解密auth_data
		if ($data_is_decrypt) {
			$auth_token = data_decrypt(trim($auth_data));
			if (!$auth_token || empty($auth_token)) {
				return false;
			}
		} else {
			$auth_token = $auth_data;
		}
		$auth_token = strtolower($auth_token);
		$auth_info = Db::name('supplier_auth')->where(['auth_token' => self::_clientAuthTokenEncode($auth_token)])->find();

		if (!$auth_info || empty($auth_info)) {
			// $log = ['user_id' => 0, 'action_info' => '用户尝试AuthData登陆失败，账户无效或被回收 [' . $auth_token . "]"];
			// Hook::listen('user_log_action', $log);
			return false;
		} else if (!isset($auth_info['expire_time']) || $auth_info['expire_time'] < TIMESTAMP) {
			// $log = ['user_id' => $auth_info['user_id'], 'action_info' => '用户尝试AuthData登陆失败，会话已过期 [' . $auth_token . "]"];
			// Hook::listen('user_log_warning', $log);
			return false;
		}
		//处理登陆状态
		//标记用户会话状态及信息
		// $SysUserModel = new SysUserModel();
		// $user_info = $SysUserModel->getUserInfo($auth_info['user_id']);
        
        $supplier_info = Db::name('supplier_user')->where(['id'=>$auth_info['supplier_user_id']])->find(); 

		if (!is_array($supplier_info) || empty($supplier_info)) {
			self::$_error[] = 'AuthData登陆失败，账户不存在';
			return false;
		}

		if (!$supplier_info['status']) {
			self::$_error[]='登录失败: 账户已禁用';
			return false;
		}
		//保持客户端Token不变，已保持同一终端设备会话持续有效
		if (self::_setSessionCookie($supplier_info, 'token_keep', ['auth_token' => $auth_token])) {
			
			return true;
		} else {

			return false;
		}
	}

	/**
	 * 用登录接口（账户密码）
	 * @param $username string|integer
	 * @param $password string
	 * @param $option array
	 * @return bool; 
	 */
	final public static function loginPassword($username = '', $password = '',$option = ['has_online' => true, 'is_remember' => true, 'is_sha256' => false])
	{
		$username = strip_tags(trim($username));
		$username = preg_replace("/\s|　/", "", $username); //所有空格+全角空格
		$password = trim($password);
		if (empty($username)) {
			self::$_error[] = '登陆失败: 账户信息未填写';
			return false;
		}
		if (empty($password)) {
			self::$_error[] = '登陆失败: 账户密码未填写';
			return false;
		}

		if (!is_array($option)) {
			$option = ['is_remember' => false, 'is_sha256' => false];
		} else {
			if (!isset($option['has_online'])) {
				$option['has_online'] = true;
			}
			if (!isset($option['is_remember'])) {
				$option['is_remember'] = false;
			}
			if (!isset($option['is_sha256'])) {
				$option['is_sha256'] = false;
			}
		}

		$supplier_info = Db::name('supplier_user')
		->alias('SU')
        ->join('supplier S', 'SU.supplier_id = S.supplier_id', 'LEFT')
		->where(['SU.username'=>$username,'SU.password'=>md5($password)])
		->find();


		if (empty($supplier_info)) {
			self::$_error[]='登录失败: 账户不存在';
			return false;
		}

		if (!$supplier_info['supplier_status']) {
			self::$_error[]='登录失败: 供应商已被禁用';
			return false;
		}

		if (!$supplier_info['status']) {
			self::$_error[]='登录失败: 供应商账户已禁用';
			return false;
		}

		if (self::_setSessionCookie($supplier_info,'create',$option)) {
			return true;
		}else{
			return false;
		}

	}


	/**
	 * 创建、更新标记用户登陆成功状态（Session/Cookie）
	 * @param $supplier_info array 用户基本信息
	 * @param $mode string 创建类型 create|update|token_keep
	 * @param $_option array 选项
	 * @return bool
	 */

	private static function _setSessionCookie($supplier_info = array(), $mode = "create", $_option = ['is_remember' => true, 'has_online' => true, 'auth_token' => '', 'auth_type' =>0,'client_device' => -1])
	{
		$option = ['is_remember' => true,'has_online' => true, 'auth_token' => '', 'auth_type'=>0,'client_device' => 0];
		if (is_array($_option)) {
			$option = array_merge($option, $_option);
		}

		//OpenAPI接口CURL请求处理，这里为了兼容实时数据，强制初始化静态类基础参数
		self::_initCall(true);
		if (empty($supplier_info) || !is_array($supplier_info) || !isset($supplier_info['id'])) {
			self::$_error[] = '[SSC] 用户数据不完整或缺失';
			return false;
		}
		//这里为了兼容OpenApi如有client_device约定则按约定
		if ($option['client_device'] == -1) {
			$option['client_device'] = get_client_device(1);
		} else if (is_numeric($option['client_device'])) {
			$option['client_device'] = get_client_device_by_id(1);
		} else if (is_string($option['client_device']) && !empty($option['client_device'])) {
			$option['client_device'] = get_client_device_by_name($option['client_device']);
		} else {
			$option['client_device'] = get_client_device(1);
		}
		if ($mode == 'create') {
			//[ 每次登陆时重新生成 AuthToken  用户加密为交互私有Key ]，防止Session/Cookie伪造，禁止同一账户同一终端多重登陆
			$auth_token = self::_createAuthToken($supplier_info['id'],$option['auth_type']);

			if ($auth_token === false) {
				return false;
			}
		} elseif ($mode == 'update') {
			if (!is_array(self::$_account) || !isset(self::$_account['auth_token'])) {
				self::$_error[] = '[SSC] 用户原Session获取失败';
				return false;
			}
			$auth_token = self::$_account['auth_token'];//保持不变
		} elseif ($mode == 'token_keep') {
			//Token认证时，必须指定auth_token和client_device，才能保持交互Token不变
			$auth_token = strtolower(trim($option['auth_token']));//保持不变
			if ($auth_token == '' || strlen($auth_token) != 64) {
				self::$_error[] = '[SSC] 用户Token数据不完整';
				return false;
			}
		} else {
			self::$_error[] = '[SSC] 标记用户登陆成功状态失败：创建类型无效';
			return false;
		}

        $auth_info = Db::name('supplier_auth')->where(['supplier_user_id'=>$supplier_info['id'],'auth_token'=>self::_clientAuthTokenEncode($auth_token)])->find();

        if (!$auth_info || empty($auth_info)) {
            // $log = ['user_id' => 0, 'action_info' => '用户尝试AuthData登陆失败，账户无效或被回收 [' . $auth_token . "]"];
            // Hook::listen('user_log_action', $log);
            return false;
        }

		if ($mode == 'token_keep') {
			$account_exptime = TIMESTAMP + 60 * 60 * 24;  //默认，用户保持会话状态，直到用户主动退出(最长24小时)
		} elseif ($option['is_remember']) {
			$account_exptime =TIMESTAMP + 60 * 60 * 24; //默认，用户保持会话状态，直到用户主动退出(最长24小时)
		} else {
			$account_exptime = TIMESTAMP + self::$_auth_expire_interval; //默认(900)：用户保持15分钟的活动状态，超过时间后将会自动做不在线处理
		}
   
        Db::name('supplier_auth')->where(['supplier_user_id'=>$supplier_info['id'], 'auth_token' => self::_clientAuthTokenEncode($auth_token)])->update(['expire_time' => $account_exptime, 'has_online' => 1]);
        

		//设置和更新Session
		$data = array();
		//供应商账户信息
		$data['supplier_user_id'] = $supplier_info['id'];
		$data['avatar'] = $supplier_info['avatar'];
		$data['phone'] = $supplier_info['phone'];
		$data['auth_token'] = $auth_token;

		//供应商信息
		$data['supplier_id'] = $supplier_info['supplier_id'];
		$data['supplier_name'] = $supplier_info['supplier_name'];
		$data['charge'] = $supplier_info['charge'];
		$data['charge_phone'] = $supplier_info['charge_phone'];
		$data['remark'] = $supplier_info['remark'];
		$data['admin_id'] = $supplier_info['admin_id'];
        //登录信息
		$data['has_online'] = 1;
		$data['activity_time'] = TIMESTAMP;
		$data['activity_ip'] = self::$_client_ip;
		$data['is_super'] = $supplier_info['is_super'];
		
		//为了防止Session伪造，数据签名认证
		$account_sign = data_auth_sign($data);
		session('account', $data);
		session('account_sign', $account_sign);
		session('account_exptime', $account_exptime); //更新Session过期时间
		self::$_account = session('account');
		//是否需要设置Cookie,保持自动登录状态
		if ($option['is_remember']) {
			$cookie_time_out=60 * 60 * 24;// 秒 (不用加time时间戳）
			$cookie_data = data_encrypt($auth_token, '', $cookie_time_out);
			cookie('client_account', $cookie_data, $cookie_time_out);
		}
		
		return true;
	}

	/**
	 * 创建用户回话私钥
	 * @param int $user_id
	 * @param int $auth_type
	 * @param int $has_online
	 * @return bool|string
	 */
	private static function _createAuthToken($user_id, $auth_type = 0, $has_online = 1)
	{
		$user_id = intval($user_id);
		$auth_type = intval($auth_type);
		if($auth_type<0){
			$auth_type=0;
		}else if($auth_type>2){
			$auth_type=0;
		}
		$has_online = intval($has_online) ? 1 : 0;
		//随机因子
		$auth_token = microtime() . uniqid();
		//美国安全散列算法 1 计算字符串的 sha226 散列值 64 字符
		$auth_token = strtolower(hash('sha256', $auth_token, false));

// dump(TIMESTAMP);exit();
		// 启动事务
		Db::startTrans();

		try {
			$result = Db::name('supplier_auth')->insert([
				'supplier_user_id' => $user_id,
				'auth_type' => $auth_type,
				'auth_token' => self::_clientAuthTokenEncode($auth_token),
				'client_device' => is_numeric(self::$_client_device) ? self::$_client_device : get_client_device(1),
				'has_online' => $has_online,
				'create_time' => TIMESTAMP,
				'activity_time' => TIMESTAMP,
				'activity_ip' => self::$_client_ip,
				'expire_time' => TIMESTAMP + self::$_auth_expire_interval
			]);
			if ($result) {
				// 提交事务
				Db::commit();
				return $auth_token;
			}else{
				self::$_error[] = "创建用户回话私钥失败";
				// 回滚事务
				Db::rollback();
				return false;
			}
		} catch (\Exception $e) {
			self::$_error[] = "创建用户回话私钥失败";
			// 回滚事务
			Db::rollback();
			return false;
		}
	}

	//获得客户端AuthToken编码（排除客户端移植Token到其他设备使用）
	final public static function _clientAuthTokenEncode($client_auth_token = '')
	{
		return strtolower(hash('sha256', $client_auth_token . self::$request->header('user-agent'), false));
	}


	final public static function logout()
	{
		if (self::$_is_login) {
			
			// Db::name('sys_user')->where(['user_id'=>self::$_account['user_id']])->update(['has_online'=>0]);
			Db::name('supplier_auth')->where(['supplier_user_id' => self::$_account['supplier_user_id'], 'auth_token' => self::_clientAuthTokenEncode(self::$_account['auth_token'])])->delete();
		}
		session('account', null);
		session('account_sign', null);
		session('account_exptime', null);
		cookie('client_account', null);
		return true;
	}

	final public static function updateSelfSessionCookie(){
		self::_initCall();
		if (!self::$_is_login) {
			self::$_error[] = '用户会话异常，更新账户状态失败';
			return false;
		}

		$supplier_info =Db::name('supplier_user')->where(['id'=>self::$_account['supplier_user_id']])->find();
		$option=['is_remember'=>self::$_account['is_remember']];
		return self::_setSessionCookie($supplier_info,'update',$option);
	}


   
}